/****************************************Copyright (c)*************************
**                               版权所有 (C), 2015-2020, 涂鸦科技
**
**                                 http://www.tuya.com
**
**--------------文件信息-------------------------------------------------------
**文   件   名: protocol.c
**描        述: 下发/上报数据处理函数
**使 用 说 明 :

                  *******非常重要，一定要看哦！！！********

** 1、用户在此文件中实现数据下发/上报功能
** 2、DP的ID/TYPE及数据处理函数都需要用户按照实际定义实现
** 3、当开始某些宏定义后需要用户实现代码的函数内部有#err提示,完成函数后请删除该#err
**
**--------------当前版本修订---------------------------------------------------
** 版    本: v1.0.1
** 日　  期: 2020年2月26日
** 修改人员: 周猛
** 协议版本: v1.1.1
** 描　  述: 1:产品信息新增tp字段和p字段
             2:修改product_info_update函数，新增参数ver来选择是否支持获取产品pid
               将组包格式由JSON格式换成sprintf，以减轻MCU资源压力
             3:增加带传入帧版本的串口数据发送函数wifi_uart_write_frame_ver
             4:在wifi_uart_service函数中，取消了串口数据对帧版本的校验

** 版    本: v1.0.0
** 日　  期: 2019年7月27日
** 修改人员: 陈力恒
** 协议版本: v1.1.1
** 描　  述: 1:gateway通用协议mcu_sdk初版
**-----------------------------------------------------------------------------
******************************************************************************/

#include "wifi.h"
#include "tuya_uart.h"
#include "config.h"
#include "sub_device.h"

#include "rtc.h"

/******************************************************************************
                                移植须知:
1:MCU必须在while中直接调用mcu_api.c内的wifi_uart_service()函数
2:程序正常初始化完成后,建议不进行关串口中断,如必须关中断,关中断时间必须短,关中断会引起串口数据包丢失
3:请勿在中断/定时器中断内调用上报函数
******************************************************************************/
         
/******************************************************************************
                              第一步:初始化
1:在需要使用到wifi相关文件的文件中include "wifi.h"
2:在MCU初始化中调用mcu_api.c文件中的wifi_protocol_init()函数
3:将MCU串口单字节发送函数填入protocol.c文件中uart_transmit_output函数内,并删除#error
4:在MCU串口接收函数中调用mcu_api.c文件内的uart_receive_input函数,并将接收到的字节作为参数传入
5:单片机进入while循环后调用mcu_api.c文件内的wifi_uart_service()函数
******************************************************************************/

/******************************************************************************
                        1:dp数据点序列类型对照表
          **此为自动生成代码,如在开发平台有相关修改请重新下载MCU_SDK**         
******************************************************************************/



/******************************************************************************
                           2:串口单字节发送函数
请将MCU串口发送函数填入该函数内,并将接收到的数据作为参数传入串口发送函数
******************************************************************************/

/*****************************************************************************
函数名称 : uart_transmit_output
功能描述 : 发数据处理
输入参数 : value:串口收到字节数据
返回参数 : 无
使用说明 : 请将MCU串口发送函数填入该函数内,并将接收到的数据作为参数传入串口发送函数
*****************************************************************************/
void uart_transmit_output(unsigned char value)
{
  tuya_uart_send_byte(value);

}
/******************************************************************************
                           第二步:实现具体用户函数
1:APP下发数据处理
2:数据上报处理
******************************************************************************/

/******************************************************************************
                            1:所有数据上报处理
当前函数处理全部数据上报(包括可下发/可上报和只上报)
  需要用户按照实际情况实现:
  1:需要实现可下发/可上报数据点上报
  2:需要实现只上报数据点上报
此函数为MCU内部必须调用
用户也可调用此函数实现全部数据上报
******************************************************************************/

/*****************************************************************************
函数名称 : all_data_update
功能描述 : 系统所有dp点信息上传,实现APP和muc数据同步
输入参数 : 无
返回参数 : 无
使用说明 : 此函数SDK内部需调用;
           MCU必须实现该函数内数据上报功能;包括只上报和可上报可下发型数据
*****************************************************************************/
void all_data_update(void)
{
  //#error "请在此处理可下发可上报数据及只上报数据示例,处理完成后删除该行"

  //请根据dpid按照实际数据修改每个可下发可上报函数和只上报函数，并且要根据子设备id把子设备的状态全部都上报上去
  //例如：收到该命令后，轮询所有子设备，把每个子设备下面的dpid全部上报给模块
  //调用接口为STATE_UPLOAD_CMD命令字对应的               mcu_dp_value_update  mcu_dp_bool_update等函数
  //具体使用哪个函数请根据实际情况调用
  /*
    例如用户在subid为"123456"下面创建了四个dp如下
  //总开关(可下发可上报)
  //备注:
  #define DPID_SWITCH 1
   //喷雾开关(可下发可上报)
   //备注:
  #define DPID_SWITCH_SPRAY 2
   //喷雾模式(可下发可上报)
   //备注:
  #define DPID_MODE 3
   //喷雾倒计时(可下发可上报)
   //备注:
  #define DPID_COUNTDOWN_SET 4
  
  在这边要把这个subid下的子设备状态上报上去
  mcu_dp_bool_update(DPID_SWITCH,当前总开关,my_strlen("123456"),"123456"); //BOOL型数据上报;
  mcu_dp_bool_update(DPID_SWITCH_SPRAY,当前喷雾开关,my_strlen("123456"),"123456"); //BOOL型数据上报;
  mcu_dp_enum_update(DPID_MODE,当前喷雾模式,my_strlen("123456"),"123456"); //枚举型数据上报;
  mcu_dp_enum_update(DPID_COUNTDOWN_SET,当前喷雾倒计时,my_strlen("123456"),"123456"); //枚举型数据上报;
  */

  gd_printf(("report all dev data point\r\n"));
  wait_poll_all_subdev_status_for_sync();
 // mcu_dp_bool_update(1,1,my_strlen("2x2lw2mizrf6qs5v"),"2x2lw2mizrf6qs5v"); //BOOL型数据上报;
/* 
  //此代码为平台自动生成，请按照实际数据修改每个可下发可上报函数和只上报函数
[TEMPLATE_DP_UPDATE]
*/
}

/******************************************************************************
                                WARNING!!!    
                            2:所有数据上报处理
自动化代码模板函数,具体请用户自行实现数据处理
******************************************************************************/


/******************************************************************************
                                WARNING!!!                     
此代码为SDK内部调用,请按照实际dp数据实现函数内部数据
******************************************************************************/


/*****************************************************************************
函数名称 : subdevice_add_result
功能描述 : wifi模块是否接收子设备入网请求结果通知（子设备是否实
           际入网成功需要查看ADD_DEV_RESULT_CMD命令字相关的命令）
输入参数 : result:模块是否接收子设备入网请求结果;0:接收请求/1:拒绝请求
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void subdevice_add_result(unsigned char result)
{
  //#error "请自行实现子设备添加成功/失败代码,完成后请删除该行"

  //注意：此处结果只是wifi模块是否接收子设备的入网请求，子设备实际是否入网成功需要
  //查看ADD_DEV_RESULT_CMD宏（返回添加设备结果）所对应的结果
  if(result == 0)
  {
     //wifi模块接收子设备入网请求
     gd_printf(("tuya allow subdev to add in net\r\n"));
     
  }
  else
  {
     //wifi模块拒绝子设备入网请求
     gd_printf(("tuya reject sub dev's join net\r\n"));
  }
  
}

/*****************************************************************************
函数名称 : subdevice_delete
功能描述 : wifi模块发送关闭入网指令给网关,此函数内获取需要删除
           的子设备id和子设备key，用户需要根据这两条信息自行删除子设备
输入参数 : data_buf：接收到的data部分的首地址
           data_len：接收到的data部分的长度
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void subdevice_delete(unsigned char* data_buf,unsigned short data_len)
{
  //#error "请自行实现子设备删除成功/失败代码,完成后请删除该行"
  
  cJSON *root = NULL,*item = NULL;
  
  ///////////////////////下面两条是需要删除的子设备的信息//////////////////////////
  char* sub_id = NULL;  //需要删除的的子设备id
  char* devkey = NULL;  //需要删除的子设备key
  
  unsigned char pstr[data_len + 1];
  my_memset(pstr, 0x00, data_len+1);
  my_memcpy(pstr, data_buf, data_len);

  root = cJSON_Parse(pstr);
  if(NULL == root){
      goto ADD_SUB_ERR;
  }

  item = cJSON_GetObjectItem(root, "sub_id");
  if(NULL == item){
      gd_printf(("cj get item sub_id is failed\r\n"));
      goto ADD_SUB_ERR;
  }
  sub_id = item->valuestring;

  item = cJSON_GetObjectItem(root, "devkey");
  if(NULL == item){
      gd_printf(("cj get item devkey is failed because this gateway have no devkey\r\n"));
      //goto ADD_SUB_ERR;
  }
  //devkey = item->valuestring;

  ///////////////请在此处根据获取到的sub_id和devkey删除对应的子设备/////////////////
  gd_printf(("delete sub dev id:%s\r\n",sub_id));
  uint32_t id;
  StringToHex(sub_id, (uint8_t *)&id, NULL);
  
  if(remove_sub_dev_in_flash(id))
  {
     HexDataSwitchPosition(&id);
     start_subdev_unbind((uint8_t *)&id, RF_CMD_SEND_TIME);
  }
  cJSON_Delete(root);
  return;
  ADD_SUB_ERR:
  if(NULL != root) {
      cJSON_Delete(root);
  }
}

/*****************************************************************************
函数名称 : heart_beat_check 
功能描述 : wifi模块WIFI 模组定时（1-3 分钟）检测子设备在线状态。
输入参数 : data_buf：接收到的data部分的首地址
           data_len：接收到的data部分的长度
返回参数 : 无
使用说明 : MCU需要自行调用接口实现该功能
*****************************************************************************/
void heart_beat_check(unsigned char* data_buf,unsigned short data_len)
{
  cJSON *root = NULL,*item = NULL;
  char* sub_id = NULL;          //需要检测心跳的子设备id
  
  unsigned char pstr[data_len + 1];
  my_memset(pstr, 0x00, data_len+1);
  my_memcpy(pstr, data_buf, data_len);
  root = cJSON_Parse(pstr);
  if(NULL == root){
      goto ADD_SUB_ERR;
  }

  item = cJSON_GetObjectItem(root, "sub_id");
  if(NULL == item){
      printf("cj get item sub_id is failed");
      goto ADD_SUB_ERR;
  }
  
  sub_id = item->valuestring;

  //#error "请自行实现心跳检测代码,完成后请删除该行"

  /////////////////////////请在此处根据获取到的sub_id进行子设备心跳的回复////////////////////////
  //////////////////在线的设备需要回复心跳，连续2  个心跳周期不回复则认为该设备离线////////////////
  //这边只给出示例，具体用法请在mcu_api.c里面的heart_beat_report查看函数说明
  //例如回复某个子设备的心跳间隔为60s
  if(subdev_onoff_line_judge(sub_id))
  {
     heart_beat_report(sub_id,30);
  }  

  cJSON_Delete(root);
  return;
  ADD_SUB_ERR:
  if(NULL != root) {
      cJSON_Delete(root);
  }
}

#ifdef SUPPORT_GREEN_TIME
/*****************************************************************************
函数名称 : mcu_get_greentime
功能描述 : 获取到的格林时间
输入参数 : 无
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void mcu_get_greentime(unsigned char time[])
{
  /*
  time[0]为是否获取时间成功标志，为 0 表示失败，为 1表示成功
  time[1] 为 年 份 , 0x00 表 示2000 年
  time[2]为月份，从 1 开始到12 结束
  time[3]为日期，从 1 开始到31 结束
  time[4]为时钟，从 0 开始到23 结束
  time[5]为分钟，从 0 开始到59 结束
  time[6]为秒钟，从 0 开始到59 结束
*/
  if(time[0] == 1)
  {
    //正确接收到wifi模块返回的格林数据
  }
  else
  {
  	//获取格林时间出错,有可能是当前wifi模块未联网
		gd_printf(("green time get failed\r\n"));
  }
}
#endif

#ifdef SUPPORT_MCU_RTC_CHECK
/*****************************************************************************
函数名称 : mcu_write_rtctime
功能描述 : MCU校对本地RTC时钟
输入参数 : 无
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void mcu_write_rtctime(unsigned char time[])
{
  /*
  time[0]为是否获取时间成功标志，为 0 表示失败，为 1表示成功
  time[1] 为 年 份 , 0x00 表 示2000 年
  time[2]为月份，从 1 开始到12 结束
  time[3]为日期，从 1 开始到31 结束
  time[4]为时钟，从 0 开始到23 结束
  time[5]为分钟，从 0 开始到59 结束
  time[6]为秒钟，从 0 开始到59 结束
  time[7]为星期，从 1 开始到 7 结束，1代表星期一
 */
  if(time[0] == 1)
  {
    //正确接收到wifi模块返回的本地时钟数据 
    
	  // rtc_set_time(time[1],time[2],time[3],time[4],time[5],time[6],time[7]);
  }
  else
  {
    	//获取本地时钟数据出错,有可能是当前wifi模块未联网
		  gd_printf(("local time get failed\r\n"));
		  mcu_get_system_time();
  }
}
#endif

/*****************************************************************************
函数名称 : batch_add_dev_result
功能描述 :批量添加同一个pid的多个子设备请求结果通知
输入参数 : result:模块是否接收子设备添加请求结果;0:接收请求/1:拒绝请求
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void batch_add_dev_result(unsigned char result)
{
  //#error "请自行实现批量添加同一个pid的多个子设备请求结果处理,完成后请删除该行"

  if(result == 0)
  {
    //批量添加子设备数据接收成功
    gd_printf(("add lots of sub dev success\r\n"));
  }
  else
  {
    //批量添加子设备数据接收失败
    gd_printf(("add lots of sub dev failed\r\n"));
  }
}

/*****************************************************************************
函数名称 : user_add_result_deal
功能描述 : 设备添加完成后，通知 MCU 添加结果
输入参数 : cids:子设备集合
           result：0成功，非0失败
           num:子设备的数量
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void user_add_result_deal(char* cids[],char* result,char num)
{
 // #error "请自行实现设备添加结果代码,完成后请删除该行"

  //例如cids["12","34","56"],result[0,0,1],则代表“12”对应的子设备添加成功 
  // “34”对应的子设备添加成功 “56”对应的子设备添加失败
  for(uint8_t i=0; i<num; i++)
  {
     //gd_printf(("id=%s,result=%d,num=%d\r\n",cids[i],result[i],num));
     if((result[i]==0)&&(is_id_equal(cids[i])))
     {
        record_joinin_net_success_subdev_in_flash();
        
        start_subdev_searching_or_abort();
     }  
  }
}

/*****************************************************************************
函数名称 : group_subdev_delete
功能描述 : 设备添加完成后，通知 MCU 添加结果
输入参数 : data_buf：接收到的data部分的首地址
           data_len：接收到的data部分的长度
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void add_dev_result_get(unsigned char* data_buf,unsigned short data_len)
{
  //#error "请自行实现设备添加结果代码,完成后请删除该行"
  gd_printf(("sub dev add result=%s,len=%d\r\n",data_buf,data_len));
  cJSON *root = NULL,*item,*cid_arr,*result_arr;
  unsigned int cid_arr_sz, result_arr_sz,i;
  /////////////////////////////////////////////////
  char** cids;  //要加入群组的子设备集合(sub_id集合)
  char* result;
  
  unsigned char pstr[data_len + 1];
  my_memset(pstr, 0x00, data_len+1);
  my_memcpy(pstr, data_buf, data_len);

  root = cJSON_Parse(pstr);
  if(NULL == root){
      goto ADD_SUB_ERR;
  }
    
  item = cJSON_GetObjectItem(root, "cids");
  if(NULL == item){
    gd_printf(("cj get item cids is failed\n"));
    goto ADD_SUB_ERR;
  }
  
  cid_arr    = item;
  cid_arr_sz = cJSON_GetArraySize(cid_arr);
  //cids = (char**) malloc(sizeof(char *) * cid_arr_sz);
  cids = (char**) mem_malloc(sizeof(char *) * cid_arr_sz);
  if(NULL == cids) {
    gd_printf(("malloc fail\n"));
    return;
  }
  
  for(i=0; i<cid_arr_sz; i++){
      item = cJSON_GetArrayItem(cid_arr, i);
      if(NULL == item){
          gd_printf(("cJSON_GetArrayItem cid_arr failed, item:%d\n", i));
          continue;
      }
      cids[i] = item->valuestring;
  }
  
  item = cJSON_GetObjectItem(root, "rets");
  if(NULL == item){
    gd_printf(("cj get item cids is failed\n"));
    goto ADD_SUB_ERR;
  }
  
  result_arr = item;
  result_arr_sz = cJSON_GetArraySize(result_arr);
  
  //result = (char*)malloc(sizeof(result_arr_sz));
  result = (char*)mem_malloc(sizeof(result_arr_sz));
  if(NULL == result) {
    gd_printf(("malloc fail\n"));
    return;
  }
  
  for(i=0; i<result_arr_sz; i++){
    item = cJSON_GetArrayItem(result_arr, i);
    if(NULL == item){
      gd_printf(("cJSON_GetArrayItem cid_arr failed, item:%d\n", i));
      continue;
    }
    result[i] = item->valueint;
  }
  
  /////////////////////////////////用户需要在此进行设备添加结果的处理/////////////////////////////////
  user_add_result_deal(cids,result,result_arr_sz);

  ////////////////////////////////用户需要在此进行设备添加结果的处理//////////////////////////////////
  wifi_uart_write_frame(ADD_DEV_RESULT_CMD,0);

  mem_free(result);
  mem_free(cids);
  cJSON_Delete(root);
  return;
  ADD_SUB_ERR:
  if(NULL != root) {
      cJSON_Delete(root);
  }
}

#ifdef WIFI_TEST_ENABLE
/*****************************************************************************
函数名称 : wifi_test_result
功能描述 : wifi功能测试反馈
输入参数 : result:wifi功能测试结果;0:失败/1:成功
           rssi:测试成功表示wifi信号强度/测试失败表示错误类型
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void wifi_test_result(unsigned char result,unsigned char rssi)
{
 // #error "请自行实现wifi功能测试成功/失败代码,完成后请删除该行"

  if(result == 0)
  {
    //测试失败
    if(rssi == 0x00)
    {
      //未扫描到名称为tuya_mdev_test路由器,请检查
    }
    else if(rssi == 0x01)
    {
      //模块未授权
    }
  }
  else
  {
    //测试成功
    //rssi为信号强度(0-100, 0信号最差，100信号最强)
  }
  
}
#endif

/*****************************************************************************
函数名称 : wifi_status_result
功能描述 : 获取 WIFI 状态结果
输入参数 : result:指示 WIFI 工作状态
           0x00:wifi状态 1 smartconfig 配置状态
           0x01:wifi状态 2 AP 配置状态
           0x02:wifi状态 3 WIFI 已配置但未连上路由器
           0x03:wifi状态 4 WIFI 已配置且连上路由器
           0x04:wifi状态 5 已连上路由器且连接到云端
           0x05:wifi状态 6 WIFI 设备处于低功耗模式
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void wifi_status_result(unsigned char result)
{
 // #error "请自行实现WIFI 工作状态结果代码,完成后请删除该行"

  switch(result) {
    case 0:
      //wifi工作状态1
    break;
    case 1:
      //wifi工作状态2
    break;
    case 2:
      //wifi工作状态3
    break;
    case 3:
      //wifi工作状态4
    break;
    case 4:
      //wifi工作状态5
    break;
    case 5:
      //wifi工作状态6
    break;
  }
  
}

/*****************************************************************************
函数名称 : inform_dev_del_status
功能描述 : 1 网关收到移除或者恢复出厂命令时，会向 MCU 报告状态。
           2 如果仅收到移除指令，网关只会移除配网，并不会删除子设备信息。
           下次联网时，子设备信息仍会在 app 上显示。
           3 如果收到恢复出厂指令，网关会移除配网并且清除所有子设备信息。
           
输入参数 : result:移除方式
           0x00 本地恢复出厂
           0x01 远程移除
           0x02 本地移除
           0x03 远程恢复出厂
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void inform_dev_del_status(unsigned char result)
{
//  #error "请自行实现恢复出厂设置代码,完成后请删除该行"

  switch(result) {
    case 0:
      //本地恢复出厂
        gd_printf(("local restore factory\r\n"));
        factory_reset_by_key();
    break;
    case 1:
      //远程移除
      gd_printf(("remote remove\r\n"));
    break;
    case 2:
      //本地移除
      gd_printf(("local remove\r\n"));
    break;
    case 3:
      //远程恢复出厂
      gd_printf(("remote restore factory\r\n"));
      factory_reset_by_app();
    break;
  }
}

/*****************************************************************************
函数名称 : local_del_subdev
功能描述 : 本地删除子设备结果
输入参数 : result:本地子设备删除结果结果;0:成功/1:失败
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void local_del_subdev(unsigned char result)
{
 // #error "请自行实现wifi功能测试成功/失败代码,完成后请删除该行"

  if(result == 0)
  {
    //测试成功
  }
  else
  {
    //测试失败
  }
}

/*****************************************************************************
函数名称 : local_subdev_limit
功能描述 : 本地允许/关闭添加子设备结果通知
输入参数 : result:
           0：本地允许/关闭添加子设备成功
           1：本地允许/关闭添加子设备失败
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void local_subdev_limit(unsigned char result)
{
  //#error "请自行实现本地允许/关闭添加子设备结果通知代码,完成后请删除该行"

  if(result == 0)
  {
    //本地允许/关闭添加子设备成功
    gd_printf(("tuya respond add subdev cmd allow\r\n"));
  }
  else
  {
    //本地允许/关闭添加子设备失败
    gd_printf(("tuya respond add subdev cmd forbid\r\n"));
  }
}

/*****************************************************************************
函数名称 : remain_memory_result
功能描述 : 获取模块内存
输入参数 : module_memory:获取模块内存的大小
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void remain_memory_result(unsigned int module_memory)
{
 // #error "请自行实现获取模块内存处理代码,完成后请删除该行"

}

/*****************************************************************************
函数名称 : local_subdev_limit
功能描述 : 子设备列表
输入参数 : subdev_list_buf：存放子设备的buf
           buf_len：子设备buf的长度
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void local_subdev_list(unsigned char *subdev_list_buf,unsigned short buf_len)
{
  unsigned char package_len;//包长度
  unsigned char **dev_id_table;//这个里面就是查询到的子设备列表
  unsigned char offset = 0;
  unsigned char i;
  tSUB_LIST* sub_list = NULL;
  tSUB_NODE **sub_node;            //这个里面存放的就是
  
  sub_list = (tSUB_LIST*)subdev_list_buf;

  
  dev_id_table = (unsigned char**) mem_malloc(sizeof(char *) * sub_list->num);
  //dev_id_table = (unsigned char**) malloc(sizeof(char *) * sub_list->num);
  if(NULL == dev_id_table) {
    printf("malloc fail\n");
    return;
  }
  
  sub_node = (tSUB_NODE**)mem_malloc(sizeof(char *) * sub_list->num);
  //sub_node = (tSUB_NODE**)malloc(sizeof(char *) * sub_list->num);
  if(NULL == sub_node) {
    printf("malloc fail\n");
    return;
  }
  
  for(i = 0;i < sub_list->num;i ++) {
    sub_node[i] = (tSUB_NODE *)&(sub_list->sub_list[offset]);
    dev_id_table[i] = sub_node[i]->id; //把id的首地址指针赋值给这个指针数组
    offset += sizeof(tSUB_NODE) + sub_node[i]->len; //偏移到下一个节点的位置
  }
  
  //#error "请自行实现子设备列表代码,完成后请删除该行"

  if(sub_list->sub_package == FALSE) {        //代表后续以及没有包了，结束发送
    //当前包序号为sub_list->idx
    //用户自行存储dev_id_table中的子设备信息

  }else {        //后续还有包，下次还会调用该函数
    //包序号为sub_list->idx
    //用户自行存储dev_id_table中的子设备信息

  }

  mem_free(sub_node);
  mem_free(dev_id_table);
}

#ifdef SUPPORT_MCU_FIRM_UPDATE
/*****************************************************************************
函数名称 : upgrade_package_choose
功能描述 : 升级包大小选择
输入参数 : package_sz:升级包大小
           0x00：默认 256byte
           0x01：512byte 
           0x02：1024byte

返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void upgrade_package_choose(unsigned char package_sz)
{
  unsigned short length = 0;
  length = set_wifi_uart_byte(length,package_sz);
  
  wifi_uart_write_frame(UPDATE_START_CMD,length);
}

/*****************************************************************************
函数名称 : subdev_upgrade_package_choose
功能描述 : 升级包大小选择
输入参数 : package_sz:升级包大小
           0x00：默认 256byte
           0x01：512byte 
           0x02：1024byte
           sub_dev_id:子设备id
           sub_dev_len:子设备长度

返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void subdev_upgrade_package_choose(unsigned char package_sz,unsigned char*sub_dev_id,unsigned char sub_dev_len)
{
  #error "请自行实现子设备升级代码,完成后请删除该行"
  ////////////此次升级是针对对应的子设备的，子设备处理用户自行实现/////////////////
  
  unsigned short length = 0;
  length = set_wifi_uart_byte(length,package_sz);
  wifi_uart_write_frame(UPDATE_START_CMD,length);

}
#endif

/*****************************************************************************
函数名称 : qur_subdev_ver_report
功能描述 : 查询子设备版本
输入参数 : mcu_ver:mcu版本
           sub_id:子设备id
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void qur_subdev_ver_report(unsigned char *sub_id,unsigned char *mcu_ver)
{
  unsigned short length = 0;
  
  cJSON *root;
  char* out;
  
  root = cJSON_CreateObject();
  if(NULL == root){
    printf("cJSON_CreateObject failed");
    return;
  }
  
  cJSON_AddStringToObject(root, "sub_id", sub_id);
  
  cJSON_AddStringToObject(root, "ver", mcu_ver);
  
  out = cJSON_PrintUnformatted(root);
  cJSON_Delete(root);
  if(NULL == out){
    printf("cJSON_PrintUnformatted failed");
    return;
  }
  
  length = set_wifi_uart_buffer(length, out, my_strlen(out));
  wifi_uart_write_frame(QUR_SUBDEV_VER_CMD,length);

  mem_free(out);
}

/*****************************************************************************
函数名称 : qur_subdev_ver_get
功能描述 : wifi模块查询子设备版本号
输入参数 : data_buf：接收到的data部分的首地址
           data_len：接收到的data部分的长度
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
void qur_subdev_ver_get(unsigned char* data_buf,unsigned short data_len)
{
  cJSON *root = NULL,*item = NULL;
  
  char* sub_id = NULL;  //需要检测心跳的子设备id
  
  unsigned char pstr[data_len + 1];
  my_memset(pstr, 0x00, data_len+1);
  my_memcpy(pstr, data_buf, data_len);

  root = cJSON_Parse(pstr);
  if(NULL == root){
      goto ADD_SUB_ERR;
  }

  item = cJSON_GetObjectItem(root, "sub_id");
  if(NULL == item){
      printf("cj get item sub_id is failed");
      goto ADD_SUB_ERR;
  }
  sub_id = item->valuestring;
  
 // #error "请自行实现wifi模块查询子设备版本号代码,完成后请删除该行"

  //请在此处填写子设备对应的mcu版本号
  /*
  例如mcu版本号为1.0.3
  qur_subdev_ver_report(sub_id,"1.0.3");
  */

  cJSON_Delete(root);
  return;
  ADD_SUB_ERR:
  if(NULL != root) {
      cJSON_Delete(root);
  }
}

#ifdef SUPPORT_MCU_FIRM_UPDATE
/*****************************************************************************
函数名称 : mcu_firm_update_handle
功能描述 : MCU进入固件升级模式
输入参数 : value:固件缓冲区
           position:当前数据包在于固件位置
           length:当前固件包长度(固件包长度为0时,表示固件包发送完成)
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
unsigned char mcu_firm_update_handle(const unsigned char value[],unsigned long position,unsigned short length)
{
  #error "请自行完成MCU固件升级代码,完成后请删除该行"
  if(length == 0)
  {
    //固件数据发送完成
    
  }
  else
  {
    //固件数据处理
  }
  
  return SUCCESS;
}

/*****************************************************************************
函数名称 : subdev_firm_update_handle
功能描述 : MCU进入子设备升级模式
输入参数 : sub_id_buf:子设备id
           value:固件缓冲区
           position:当前数据包在于固件位置
           length:当前固件包长度(固件包长度为0时,表示固件包发送完成)
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
unsigned char subdev_firm_update_handle(unsigned char *sub_id_buf,const unsigned char value[],unsigned long position,unsigned short length)
{
  #error "请自行完成MCU固件升级代码,完成后请删除该行"
  if(length == 0)
  {
    //固件数据发送完成
    
  }
  else
  {
    //固件数据处理
  }
  
  return SUCCESS;
}
#endif

/*****************************************************************************
函数名称 : dp_download_handle 网关本身的dpid处理
功能描述 : dp下发处理函数
输入参数 : dpid:DP序号
           value:dp数据缓冲区地址
           length:dp数据长度
返回参数 : 成功返回:SUCCESS/失败返回:ERRO
使用说明 : 该函数用户不能修改
*****************************************************************************/
unsigned char gw_dp_download_handle(unsigned char dpid,const unsigned char value[], unsigned short length)
{
  /***************************************************************************************************
  当前函数处理可下发/可上报数据调用                ,具体函数内需要实现下发数据处理
  完成用需要将处理结果反馈至APP端,否则APP会认为下发失败
  *****************************************************************************************************/  
  return gateway_handle(dpid, value, length);
}

/*****************************************************************************
函数名称 : subdev_dp_download_handle
功能描述 : dp下发子设备处理函数
输入参数 : dpid:DP序号
           value:dp数据缓冲区地址
           length:dp数据长度
           sub_id：子设备id
返回参数 : 成功返回:SUCCESS/失败返回:ERRO
使用说明 : 该函数用户不能修改
*****************************************************************************/
unsigned char subdev_dp_download_handle(unsigned char dpid,const unsigned char value[], unsigned short length,unsigned char* sub_id)
{
  //#error "请在此处理dp下发子设备处理函数,处理完成后删除该行"
  
  /***************************************************************************************************
  当前函数处理可下发/可上报数据调用                ,具体函数内需要实现下发数据处理
  完成用需要将处理结果反馈至APP端,否则APP会认为下发失败
  请在该函数根据子设备的id自行实现子设备的dpid处理
  *****************************************************************************************************/
  
  //sub_dev_id = (uint32_t)buf[0]<<24|(uint32_t)buf[1]<<16|(uint32_t)buf[2]<<8|(uint32_t)buf[3];
  gd_printf(("issue cmd sub dev datapoint=%d, value=0x%x,len=%d, sub_id_string=%s\r\n",dpid,value[0],length,sub_id));

  return sub_dev_dp_handle(dpid, value, length, sub_id);
  //此处仅给出demo，具体实现需要用户自行实现
  
}

/*****************************************************************************
函数名称 : get_download_cmd_total
功能描述 : 获取所有dp命令总和
输入参数 : 无
返回参数 : 下发命令总和
使用说明 : 该函数用户不能修改
*****************************************************************************/
unsigned char get_download_cmd_total(void)
{
  return get_download_cmd_cnt();
}


